-
Notifications
You must be signed in to change notification settings - Fork 164
Maximize API #1036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Maximize API #1036
Conversation
CountersTracker provides a flexible API for mapping program state to coverage counters, enabling incremental progress feedback to libFuzzer. Key features: - ensureCountersAllocated(id, numCounters): allocate counter range - setCounter/setCounterRange: set counter values by ID and offset - Thread-safe allocation via ConcurrentHashMap - Separate memory region from main coverage map This lays the foundation for the maximize() hill-climbing API.
97db30c to
390836b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a maximize() API to Jazzer that enables hill-climbing fuzzing scenarios where standard code coverage is insufficient. The API guides the fuzzer to maximize a value by setting coverage counters for all values from the minimum up to the observed value, creating incremental progress feedback.
Changes:
- Added
CountersTrackerinfrastructure (Java and C++) to manage extra coverage counters separate from regular code coverage - Added
Jazzer.maximize()API with automatic call-site ID generation via instrumentation hooks - Added comprehensive test coverage for the new APIs
- Added ReactorFuzzTest example demonstrating the maximize API on a chaotic feedback system
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.h | Refactored header from CoverageTracker to CountersTracker, adding support for separate extra counters region |
| src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.cpp | New implementation managing both coverage and extra counters with libFuzzer registration |
| src/main/native/com/code_intelligence/jazzer/driver/BUILD.bazel | Updated build dependencies to reference counters_tracker instead of coverage_tracker |
| src/main/java/com/code_intelligence/jazzer/runtime/CountersTracker.java | New Java class providing thread-safe counter allocation and management API |
| src/main/java/com/code_intelligence/jazzer/runtime/BUILD.bazel | Added CountersTracker build target and dependencies |
| src/main/java/com/code_intelligence/jazzer/runtime/JazzerApiHooks.java | Added instrumentation hook to auto-generate call-site IDs for maximize() calls |
| src/main/java/com/code_intelligence/jazzer/api/Jazzer.java | Added maximize() API methods with documentation |
| src/test/java/com/code_intelligence/jazzer/runtime/CountersTrackerTest.java | Comprehensive unit tests for CountersTracker including concurrency tests |
| src/test/java/com/code_intelligence/jazzer/api/MaximizeTest.java | Unit tests for the maximize() API covering edge cases |
| examples/junit/src/test/java/com/example/ReactorFuzzTest.java | Example demonstrating maximize() on a temperature maximization problem |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/main/native/com/code_intelligence/jazzer/driver/counters_tracker.cpp
Show resolved
Hide resolved
| return; | ||
| } | ||
|
|
||
| int numCounters = (int) (maxValue - minValue + 1); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integer overflow vulnerability: When (maxValue - minValue + 1) exceeds Integer.MAX_VALUE, the cast to int will silently overflow, leading to incorrect behavior. This could result in negative numCounters values or other unexpected results. The code should validate that the range fits within an int before the cast, and provide a clear error message if it doesn't. For example: if (maxValue - minValue + 1 > Integer.MAX_VALUE) throw an appropriate exception.
| int numCounters = (int) (maxValue - minValue + 1); | |
| final long range; | |
| try { | |
| range = Math.addExact(Math.subtractExact(maxValue, minValue), 1L); | |
| } catch (ArithmeticException e) { | |
| throw new IllegalArgumentException( | |
| "maxValue - minValue + 1 must fit within a 32-bit signed integer range", e); | |
| } | |
| if (range <= 0L || range > Integer.MAX_VALUE) { | |
| throw new IllegalArgumentException( | |
| "maxValue - minValue + 1 must be in the range [1, " + Integer.MAX_VALUE + "], but was " | |
| + range); | |
| } | |
| int numCounters = (int) range; |
|
|
||
| // Set counters if value provides signal | ||
| if (value >= minValue) { | ||
| int toOffset = (int) (Math.min(value, maxValue) - minValue); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Integer overflow in toOffset calculation: Similar to the numCounters calculation above, when (Math.min(value, maxValue) - minValue) exceeds Integer.MAX_VALUE, the cast to int will silently overflow. This could cause incorrect counter ranges to be set. Add validation before the cast.
| int toOffset = (int) (Math.min(value, maxValue) - minValue); | |
| long toOffsetLong = Math.min(value, maxValue) - minValue; | |
| if (toOffsetLong > Integer.MAX_VALUE) { | |
| // Avoid integer overflow when converting to int; cannot represent this offset safely. | |
| return; | |
| } | |
| int toOffset = (int) toOffsetLong; |
| Jazzer.maximize(50, 5008, 0, 100); | ||
| Jazzer.maximize(50, 5008, 0, 200); // No-op without runtime |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test issue: This test expects different behavior with and without runtime but doesn't verify either behavior. With runtime, calling maximize with different ranges for the same ID should throw an IllegalArgumentException (as stated in the comment on line 76), but the test doesn't verify this. Consider using an appropriate assertion or expectation to verify the exception is thrown when runtime is available.
| long largeValue = 750_000; | ||
| long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000); | ||
| Jazzer.maximize(step, 5014, 0, 1000); |
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test is always false.
| long largeValue = 750_000; | |
| long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000); | |
| Jazzer.maximize(step, 5014, 0, 1000); | |
| for (long largeValue : new long[] {-750_000L, 750_000L}) { | |
| long step = largeValue < 0 ? 0 : Math.min(largeValue / 1000, 1000); | |
| Jazzer.maximize(step, 5014, 0, 1000); | |
| } |
| /** Maximum number of counters available (default 1M, configurable via environment variable). */ | ||
| private static final int MAX_COUNTERS = | ||
| System.getenv(ENV_MAX_COUNTERS) != null | ||
| ? Integer.parseInt(System.getenv(ENV_MAX_COUNTERS)) | ||
| : 1 << 20; | ||
|
|
Copilot
AI
Feb 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential uncaught 'java.lang.NumberFormatException'.
| /** Maximum number of counters available (default 1M, configurable via environment variable). */ | |
| private static final int MAX_COUNTERS = | |
| System.getenv(ENV_MAX_COUNTERS) != null | |
| ? Integer.parseInt(System.getenv(ENV_MAX_COUNTERS)) | |
| : 1 << 20; | |
| private static final int DEFAULT_MAX_COUNTERS = 1 << 20; | |
| /** Maximum number of counters available (default 1M, configurable via environment variable). */ | |
| private static final int MAX_COUNTERS = initMaxCounters(); | |
| private static int initMaxCounters() { | |
| String value = System.getenv(ENV_MAX_COUNTERS); | |
| if (value == null || value.isEmpty()) { | |
| return DEFAULT_MAX_COUNTERS; | |
| } | |
| try { | |
| return Integer.parseInt(value.trim()); | |
| } catch (NumberFormatException e) { | |
| return DEFAULT_MAX_COUNTERS; | |
| } | |
| } |
Add Jazzer.maximize(value, id, minValue, maxValue) for guiding the fuzzer to maximize a value over time. For each observed value v in [minValue, maxValue], sets counters [0, v-minValue] to signal progress. Features: - Enables corpus minimization (only max-value input retained) - Convenience overload without explicit ID (uses instrumentation hook) - Delegates to CountersTracker for counter management - No state in Jazzer.java - all managed by CountersTracker
Example shows how maximize() helps fuzz a chaotic feedback system where standard coverage provides no guidance. The fuzzer is guided to increase "temperature" through complex state-dependent logic.
390836b to
ae1b9e1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (maxValue < minValue) { | ||
| throw new IllegalArgumentException("maxValue must be >= minValue"); | ||
| } | ||
| long range = maxValue - minValue; | ||
| if (range < 0 || range > (long) Integer.MAX_VALUE - 1) { | ||
| throw new IllegalArgumentException( | ||
| "Range too large: (maxValue - minValue + 1) must be <= Integer.MAX_VALUE"); | ||
| } |
Copilot
AI
Feb 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation checks for invalid ranges occur before checking if the runtime is available (COUNTERS_TRACKER_ALLOCATE == null). This causes exceptions to be thrown even when running without the Jazzer runtime, which is inconsistent with the behavior of similar methods like exploreState() and guideTowardsContainment() that return early when the runtime is unavailable. The validation should be moved after the null check, or wrapped in a try-catch that returns silently, to ensure the method is a no-op when the runtime is not available.
| endOffset = startOffset + numCounters; | ||
| if (endOffset > MAX_COUNTERS) { |
Copilot
AI
Feb 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential integer overflow on line 101: the expression startOffset + numCounters can overflow if both values are large enough. For example, if startOffset is near Integer.MAX_VALUE and numCounters is positive, the addition will overflow to a negative value, bypassing the check on line 102. This should use a check like if (startOffset > MAX_COUNTERS - numCounters) or cast to long before adding.
| @Test | ||
| public void testMaximizeInvalidRange() { | ||
| // maxValue < minValue - without runtime this is a no-op, | ||
| // with runtime CountersTracker catches "numCounters must be positive" | ||
| Jazzer.maximize(50, 5007, 100, 0); | ||
| } |
Copilot
AI
Feb 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test comment states this should be "a no-op" without runtime, but the actual behavior is that Jazzer.maximize() throws an IllegalArgumentException at line 273 before checking if the runtime is available. This test will fail when the runtime is loaded. Either the test needs to expect an exception, or the validation logic in maximize() needs to be moved after the null check for COUNTERS_TRACKER_ALLOCATE.
| @Test | ||
| public void testMaximizeLargeRange() { | ||
| // Extremely large range - without runtime this is a no-op, | ||
| // with runtime would fail in CountersTracker |
Copilot
AI
Feb 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test comment states this would "fail in CountersTracker" with runtime, but it will actually fail earlier in Jazzer.maximize() at line 277 when checking if the range is too large. The validation happens before checking if the runtime is available, so this test will throw an IllegalArgumentException with the message about range being too large, regardless of whether CountersTracker is available.
| @Test | |
| public void testMaximizeLargeRange() { | |
| // Extremely large range - without runtime this is a no-op, | |
| // with runtime would fail in CountersTracker | |
| @Test(expected = IllegalArgumentException.class) | |
| public void testMaximizeLargeRange() { | |
| // Extremely large range - Jazzer.maximize validates the range size | |
| // and throws IllegalArgumentException before interacting with CountersTracker |
Summary
Add a hill-climbing
maximize()API to Jazzer that guides the fuzzer toward maximizing a value over time. This enables fuzzing scenarios where standard code coverage provides insufficient guidance, such as finding inputs that maximize some computed metric.Changes
Jazzer.maximize()APIHow it works: For each observed value v, sets coverage counters [0, v-minValue] to 1. This creates incremental progress feedback - higher values trigger more "coverage," guiding the fuzzer toward the maximum. Corpus minimization naturally retains only the input producing the highest value.
Example
Added ReactorFuzzTest demonstrating the API on a chaotic feedback system where standard coverage is constant but the fuzzer needs to maximize a computed temperature value.